///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef _LIGHT_H__
#define _LIGHT_H__

#include "OgrePrerequisites.h"

#include "util/colourValue.h"
#include "math/vector3.h"
#include "math/vector4.h"
#include "OgreString.h"
#include "OgreMovableObject.h"
#include "math/planeBoundedVolume.h"
#include "OgreShadowCameraSetup.h"

namespace Ogre {


	
	
	
    class _OgreExport Light : public MovableObject
    {
    public:
        /// Temp tag used for sorting
        Real tempSquareDist;
		/// internal method for calculating current squared distance from some world position
		void _calcTempSquareDist(const Vector3& worldPos);

        /// Defines the type of light
        enum LightTypes
        {
            /// Point light sources give off light equally in all directions, so require only position not direction
            LT_POINT = 0,
            /// Directional lights simulate parallel light beams from a distant source, hence have direction but no position
            LT_DIRECTIONAL = 1,
            /// Spotlights simulate a cone of light from a source so require position and direction, plus extra values for falloff
            LT_SPOTLIGHT = 2
        };

        
        Light();

        
        Light(const String& name);

        
        ~Light();

        
        void setType(LightTypes type);

        
        LightTypes getType(void) const;

        
        void setDiffuseColour(Real red, Real green, Real blue);

        
        void setDiffuseColour(const ColourValue& colour);

        
        const ColourValue& getDiffuseColour(void) const;

        
        void setSpecularColour(Real red, Real green, Real blue);

        
        void setSpecularColour(const ColourValue& colour);

        
        const ColourValue& getSpecularColour(void) const;

        
        void setAttenuation(Real range, Real constant, Real linear, Real quadratic);

        
        Real getAttenuationRange(void) const;

        
        Real getAttenuationConstant(void) const;

        
        Real getAttenuationLinear(void) const;

        
        Real getAttenuationQuadric(void) const;

        
        void setPosition(Real x, Real y, Real z);

        
        void setPosition(const Vector3& vec);

        
        const Vector3& getPosition(void) const;

        
        void setDirection(Real x, Real y, Real z);

        
        void setDirection(const Vector3& vec);

        
        const Vector3& getDirection(void) const;

        
        void setSpotlightRange(const Radian& innerAngle, const Radian& outerAngle, Real falloff = 1.0);

        
        const Radian& getSpotlightInnerAngle(void) const;

        
        const Radian& getSpotlightOuterAngle(void) const;

        
        Real getSpotlightFalloff(void) const;

		
		void setSpotlightInnerAngle(const Radian& val);

		
		void setSpotlightOuterAngle(const Radian& val);

		
		void setSpotlightFalloff(Real val);

		
		void setSpotlightNearClipDistance(Real nearClip) { mSpotNearClip = nearClip; }
		
		
		Real getSpotlightNearClipDistance() const { return mSpotNearClip; }
		
		
		void setPowerScale(Real power);

		
		Real getPowerScale(void) const;

        
        void _notifyAttached(Node* parent, bool isTagPoint = false);

        
        void _notifyMoved(void);

        
        const AxisAlignedBox& getBoundingBox(void) const;

        
        void _updateRenderQueue(RenderQueue* queue);

        
        const String& getMovableType(void) const;

        
        const Vector3& getDerivedPosition(bool cameraRelativeIfSet = false) const;

        
        const Vector3& getDerivedDirection(void) const;

        
        void setVisible(bool visible);

        
        Real getBoundingRadius(void) const { return 0; /* not visible */ }

		
		Vector4 getAs4DVector(bool cameraRelativeIfSet = false) const;

        
        virtual const PlaneBoundedVolume& _getNearClipVolume(const Camera* const cam) const;

        
        virtual const PlaneBoundedVolumeList& _getFrustumClipVolumes(const Camera* const cam) const;

		/// Override to return specific type flag
		uint32 getTypeFlags(void) const;

		/// @copydoc AnimableObject::createAnimableValue
		AnimableValuePtr createAnimableValue(const String& valueName);

		
		void setCustomShadowCameraSetup(const ShadowCameraSetupPtr& customShadowSetup);

		
		void resetCustomShadowCameraSetup(void);

		
		const ShadowCameraSetupPtr& getCustomShadowCameraSetup(void) const;

		/// @copydoc MovableObject::visitRenderables
		void visitRenderables(Renderable::Visitor* visitor, 
			bool debugRenderables = false);

		
		size_t _getIndexInFrame() const { return mIndexInFrame; }
		void _notifyIndexInFrame(size_t i) { mIndexInFrame = i; }
		
        
        void setShadowFarDistance(Real distance);
        
        void resetShadowFarDistance(void);
        
        Real getShadowFarDistance(void) const;
        Real getShadowFarDistanceSquared(void) const;

		
		void setShadowNearClipDistance(Real nearClip) { mShadowNearClipDist = nearClip; }

		
		Real getShadowNearClipDistance() const { return mShadowNearClipDist; }

		
		Real _deriveShadowNearClipDistance(const Camera* maincam) const;

		
		void setShadowFarClipDistance(Real farClip) { mShadowFarClipDist = farClip; }

		
		Real getShadowFarClipDistance() const { return mShadowFarClipDist; }

		
		Real _deriveShadowFarClipDistance(const Camera* maincam) const;

		/// Set the camera which this light should be relative to, for camera-relative rendering
		void _setCameraRelative(Camera* cam);

		
        void setCustomParameter(uint16 index, const Vector4& value);

        
        const Vector4& getCustomParameter(uint16 index) const;

		
        virtual void _updateCustomGpuParameter(uint16 paramIndex, 
			const GpuProgramParameters::AutoConstantEntry& constantEntry, 
			GpuProgramParameters* params) const;
    protected:
        /// internal method for synchronising with parent node (if any)
        virtual void update(void) const;

		/// @copydoc AnimableObject::getAnimableDictionaryName
		const String& getAnimableDictionaryName(void) const;
		/// @copydoc AnimableObject::initialiseAnimableDictionary
		void initialiseAnimableDictionary(StringVector& vec) const;

		LightTypes mLightType;
        Vector3 mPosition;
        ColourValue mDiffuse;
        ColourValue mSpecular;

        Vector3 mDirection;

        Radian mSpotOuter;
        Radian mSpotInner;
        Real mSpotFalloff;
		Real mSpotNearClip;
        Real mRange;
        Real mAttenuationConst;
        Real mAttenuationLinear;
        Real mAttenuationQuad;
		Real mPowerScale;
		size_t mIndexInFrame;
		bool mOwnShadowFarDist;
		Real mShadowFarDist;
		Real mShadowFarDistSquared;
		
		Real mShadowNearClipDist;
		Real mShadowFarClipDist;


        mutable Vector3 mDerivedPosition;
        mutable Vector3 mDerivedDirection;
		// Slightly hacky but unless we separate observed light render state from main Light...
		mutable Vector3 mDerivedCamRelativePosition;
		mutable bool mDerivedCamRelativeDirty;
		Camera* mCameraToBeRelativeTo;

        /// Shared class-level name for Movable type
        static String msMovableType;

        mutable PlaneBoundedVolume mNearClipVolume;
        mutable PlaneBoundedVolumeList mFrustumClipVolumes;
        /// Is the derived transform dirty?
        mutable bool mDerivedTransformDirty;

		/// Pointer to a custom shadow camera setup
		mutable ShadowCameraSetupPtr mCustomShadowCameraSetup;

		typedef map<uint16, Vector4>::type CustomParameterMap;
		/// Stores the custom parameters for the light
        CustomParameterMap mCustomParameters;
    };

	
	class _OgreExport LightFactory : public MovableObjectFactory
	{
	protected:
		MovableObject* createInstanceImpl( const String& name, const NameValuePairList* params);
	public:
		LightFactory() {}
		~LightFactory() {}

		static String FACTORY_TYPE_NAME;

		const String& getType(void) const;
		void destroyInstance( MovableObject* obj);  

	};
	
	

} // Namespace
#endif
